import React, { useEffect, useMemo, useRef, useState } from "react";
import { Table } from "antd";
import { omit, pick, upperFirst } from "lodash-es";
import { useLoading } from "./hooks/useLoading";
import { usePagination } from "./hooks/usePagination";
import { useRowSelection } from "./hooks/useRowSelection";
import { useDataSource } from "./hooks/useDataSource";
import { useColumns } from "./hooks/useColumns";
import { useTableExpand } from "./hooks/useTableExpand";
import { useTableHeader } from "./hooks/useTableHeader";
import { useTableScroll } from "./hooks/useTableScroll";
import { useTableForm } from "./hooks/useTableForm";
import { BasicForm, useForm } from "@/components/Form";
import type { BasicTableProps, TableActionType } from "./types/table";
import { isFunction } from "@/utils/is";
import { BasicPropsType, BasicPropsDefault } from "./props";
import { childrenToMap } from "@/utils/helper/tsxHelper";
import "./style/BasicTable.less";
import { createTableContext } from "./hooks/useTableContext";

interface ComponentProps extends ComponentNode {
	// emit
	register: Fn;
	selectionChange?: Fn;
}

const defineEmits = (keyArr: string[], props: Recordable) => {
	return (key: string, ...args: any) => {
		if (!keyArr.includes(key)) return;
		const eventKey = `on${upperFirst(key)}`;
		props?.[eventKey]?.(...args);
	};
};

const BasicTable = (props: BasicPropsType & ComponentProps) => {
	// fix React18 "defaultProps" problem
	props = Object.assign({}, { ...BasicPropsDefault, ...props });

	// emit
	const emit = defineEmits(
		[
			// "fetch-success",
			// "fetch-error",
			// "selection-change",
			// "register",
			// "row-click",
			// "row-dbClick",
			// "row-contextmenu",
			// "row-mouseenter",
			// "row-mouseleave",
			"editEnd",
			"editCancel",
			// "edit-row-end",
			"editChange"
			// "expanded-rows-change",
			// "change",
			// "columns-change"
		],
		props
	);

	const slots = childrenToMap(props.children);
	const attrs = pick(props, ["className", "style"]);
	const prefixCls = "basic-table";

	const wrapRef = useRef(null);
	const formRef = useRef(null);
	const tableElRef = useRef<React.ReactNode>(null);
	const tableData = useRef<Recordable[]>([]);

	// const propsRef = useRef<Partial<BasicTableProps>>({});
	const [propsRef, setPropsRef] = useState<Partial<BasicTableProps>>();
	const [registerForm, formActions] = useForm();

	const getProps = useMemo(() => {
		return { ...props, ...propsRef } as BasicTableProps;
	}, [propsRef]);

	const { getLoading, setLoading } = useLoading(getProps);
	const { getPaginationInfo, getPagination, setPagination, setShowPagination, getShowPagination } = usePagination(getProps);

	const {
		getRowSelection,
		getRowSelectionRef,
		getSelectRows,
		setSelectedRows,
		clearSelectedRowKeys,
		getSelectRowKeys,
		deleteSelectRowByKey,
		setSelectedRowKeys
	} = useRowSelection(getProps, tableData.current, props.selectionChange);

	const {
		handleTableChange,
		getDataSourceRef,
		getDataSource,
		getRawDataSource,
		setTableData,
		updateTableDataRecord,
		deleteTableDataRecord,
		insertTableDataRecord,
		fetch,
		getRowKey,
		reload,
		// getAutoCreateKey,
		updateTableData
	} = useDataSource(getProps, {
		setTableDataRef,
		getPaginationInfo,
		setLoading,
		setPagination,
		getFieldsValue: formActions.getFieldsValue,
		clearSelectedRowKeys: function () {}
	});

	function onTableChange(pagination: any, filters: any, sorter: any, extra: any) {
		handleTableChange(pagination, filters, sorter);
		// 解决通过useTable注册onChange时不起作用的问题
		const { onChange } = getProps;
		onChange && isFunction(onChange) && onChange(pagination, filters, sorter, extra);
	}

	const {
		getViewColumns,
		getColumns,
		setCacheColumnsByField,
		setCacheColumns,
		// setColumnWidth,
		setColumns,
		getColumnsRef,
		getCacheColumns,
		updateColumn
	} = useColumns(getProps);

	const { getScrollRef, redoHeight } = useTableScroll(
		getProps,
		tableElRef.current,
		getColumnsRef,
		getRowSelectionRef,
		getDataSourceRef,
		wrapRef.current,
		formRef.current
	);

	const { getExpandOption, expandAll, expandRows, collapseAll } = useTableExpand(getProps, tableData.current);

	const { getHeaderProps } = useTableHeader(getProps, slots);

	const { getFormProps, handleSearchInfoChange, getShowForm, setShowForm } = useTableForm(getProps, slots, fetch, getLoading);

	const getBindValues = useMemo(() => {
		const dataSource = getDataSourceRef;
		let propsData: any = {
			// customRow,
			...getProps,
			...getHeaderProps,
			scroll: getScrollRef,
			loading: getLoading,
			tableLayout: "fixed",
			rowSelection: getRowSelectionRef,
			rowKey: getRowKey,
			columns: getViewColumns,
			pagination: getPaginationInfo,
			dataSource,
			// footer: unref(getFooterProps),
			...getExpandOption
		};
		propsData = omit(propsData, ["className", "style", "onChange"]);
		return propsData;
	}, [
		getDataSourceRef,
		getProps,
		getHeaderProps,
		getScrollRef,
		getLoading,
		getRowSelectionRef,
		getRowKey,
		getViewColumns,
		getPaginationInfo,
		getExpandOption
	]);

	const getWrapperClass = useMemo(() => {
		let classList = [prefixCls, attrs.className];
		if (getBindValues.useSearchForm) classList.push(`${prefixCls}-form-container`);
		if (getBindValues.inset) classList.push(`${prefixCls}--inset`);
		return classList.join(" ");
	}, [getBindValues]);

	function setProps(props: Partial<BasicTableProps>) {
		// propsRef.current = { ...propsRef.current, ...props };
		setPropsRef({ ...propsRef, ...props });
	}

	function setTableDataRef(data: Recordable[]) {
		tableData.current = data;
	}

	const tableAction: TableActionType = {
		reload,
		getSelectRows,
		setSelectedRows,
		clearSelectedRowKeys,
		getSelectRowKeys,
		deleteSelectRowByKey,
		setPagination,
		setTableData,
		updateTableDataRecord,
		deleteTableDataRecord,
		insertTableDataRecord,
		redoHeight,
		setSelectedRowKeys,
		setColumns,
		setLoading,
		getDataSource,
		getRawDataSource,
		setProps,
		getRowSelection,
		getPaginationRef: getPagination,
		getColumns,
		getCacheColumns,
		updateColumn,
		// emit,
		updateTableData,
		setShowPagination,
		getShowPagination,
		setCacheColumnsByField,
		expandAll,
		expandRows,
		collapseAll,
		// scrollTo,
		getSize: () => {
			return getBindValues.size;
		},
		setCacheColumns,
		setShowForm,
		getShowForm,
		emit
	};

	useEffect(() => {
		props.register && props.register(tableAction, formActions);
		return () => {
			// clean
			wrapRef.current = null;
			formRef.current = null;
			tableElRef.current = null;
			tableData.current = [];
		};
	}, []);

	const TableContext = createTableContext({ ...tableAction, wrapRef, getBindValues });

	return (
		<div className={getWrapperClass} ref={wrapRef}>
			<div ref={formRef}>
				{getShowForm() && getBindValues.useSearchForm && (
					<BasicForm
						submitOnReset
						{...getFormProps}
						tableAction={tableAction}
						register={registerForm}
						submit={handleSearchInfoChange}
						advancedChange={redoHeight}
					>
						{props.children}
					</BasicForm>
				)}
			</div>
			<TableContext.Provider value={{ ...tableAction, wrapRef, getBindValues }}>
				<Table {...getBindValues} ref={tableElRef} onChange={onTableChange}>
					{props.children}
				</Table>
			</TableContext.Provider>
		</div>
	);
};

export default BasicTable;
